﻿namespace NTLib.Core.Extensions
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using NTLib.Core.Reflection;
    using System.Reflection;

    /// <summary>
    /// 
    /// </summary>
    public static class ObjectExtension
    {
        #region Methods

        /// <summary>
        /// Copies the property values to another object that have or not the same type.
        /// Only public and GET/SET property will be transfered.
        /// </summary>
        /// <param name="obj">The object source.</param>
        /// <param name="destination">The object destination.</param>
        /// <param name="useConcreatType">Define if the type used to define the property will be the generic value of the real object type.</param>
        public static void CopyPropertyValuesTo<T, U>(this T obj, U destination, bool useConcreatType = false)
        {
            if (object.Equals(null, obj))
                throw new ArgumentNullException(nameof(obj));
            if (object.Equals(destination, null))
                throw new ArgumentNullException(nameof(destination));

            var propertyFromObj = (from prop in (useConcreatType ? obj.GetType() : typeof(T)).GetRuntimeProperties()
                                   where prop.CanRead && prop.CanWrite
                                   select prop);

            var propertyFromDestination = (from prop in (useConcreatType ? destination.GetType() : typeof(U)).GetRuntimeProperties()
                                           where prop.CanRead && prop.CanWrite
                                           select new { Key = prop.Name, Value = prop }).ToDictionary(k => k.Key, k => k.Value);

            foreach (var propInfo in propertyFromObj)
            {
                PropertyInfo destInfo = null;
                if (propertyFromDestination.TryGetValue(propInfo.Name, out destInfo) &&
                    destInfo.PropertyType.IsOrInheritOf(propInfo.PropertyType))
                {
                    destInfo.SetValue(destination, propInfo.GetValue(obj));
                }
            }
        }

        #endregion
    }
}
